local p=package
p.path=p.path..';../concurrentlua/src/?/init.lua;../concurrentlua/src/?.lua'

p.cpath=p.cpath..';../bin/debug/?.dll'



require'concurrent'
require'cltime'
require'socket'

concurrent.setoption('trapexit',true)

local out=io.write

port=10019
nodename='master@linkerhp'

concurrent.init(nodename)
out('concurrent node name:'..nodename.."\n")


--tcpport=socket.tcp()
--tcpport:bind("*",port)
--tcpport:settimeout(0)
require'copas'
local tcphandlers={}
local function tcphandler(sock)
	local old_sock=sock
  clientip,clientport=sock:getpeername()
  sock=copas.wrap(sock)
  sock:settimeout(0)

  clientid=clientip..':'..clientport
  tcphandlers[clientid]='connected'

	while true do

		local ret,data,err=pcall(old_sock.receive,old_sock)-- receive a line
		out'.'
		if ret==true and data then
			out('copas:\"'..data..'\"')
			out"\n"
			--[=[concurrent.send('master',{cmd='tcpdata',
													   from=sock,
													   clientid=clientid,
													   ['data']=data})
													   ]=]
		elseif err == 'timeout' then

			sock:send("Ping\r\n\r\n")
			out'p'
			--break;
		elseif err then
			print('tcp error:',err)
			break;
		elseif data then
			print(ret,err)
			break;

		end

	end
end
tcplistensock=socket.bind("*",port)
copas.addserver(tcplistensock,tcphandler)

-- udp


local udpinit=function(port)
  local u=socket.udp()
  collectgarbage('collect')
  u:settimeout(0)
  --u:setoption('reuseaddr',true)
  ret,err=u:setsockname("*",port)
  if not ret  then
    out('bind error:'..err)
    concurrent.exit('port:'..port..' bind error.')
  end
  out('udp listening at '..port..'\n')
  out'\n'
  return u;
end

local function udpfini(udpsock)
  udpsock:close()
end

local udpstep=function(udpsock)
  local data,ip,port=udpsock:receivefrom(1024)

  if data then
    --out('udp receivefrom '..ip..':'..port..' '..data..'\n')
    --out(port)
    --[=[concurrent.send('master',{cmd='udpdata',
                                                   from=udpsock,
                                                   ['clientip']=ip,
                                                   ['clientport']=port,
                                                   ['data']=data})
												   ]=]
    out'=\n'

    --udpsock:sendto(data,ip,port)
  end
  --out'udp step\n'
end

--udppkt=0
--function incudppkt() udppkt=udppkt+1 end
--function getudppkt() return udppkt end
-- concurrent main process
--local udpsock=udpinit(port)-- must be here!!!!!!!!!!!!!!!!!!!!!!!!
local master=function()
  out"master inited.\n"
  selfpid=concurrent.whereis('master')
  out('selfpid:'..selfpid..'\n')
  local udplistener={}
  local tcplistener={}

  local udpsock=udpinit(port)

  if not udpsock then concurrent.exit('udp init failed.') end

  concurrent.send('monitor',{from=selfpid,cmd='started',['udpsock']=udpsock})
  while true do
          out'M'
    copas.step(0.1)-- noblock
    udpstep(udpsock)
    local msg=concurrent.receive(1) -- timeout
    --if not msg then out'master timeout\n'; end
    --io.flush()
    if msg then
      if msg.cmd=='new udp' then

      elseif msg.cmd=='accept tcp' then

      elseif msg.cmd=='tcpdata' then
        out('master got a tcp data:'..msg.data)
        out'\n'
        msg.from:send(msg.data)
        --msg.from:close()
        --tcphandlers[msg.clientid]='disconnect'
      elseif msg.cmd=='udpdata' then
        out('udp:'..msg.clientip..':'..msg.clientport..' '..msg.data)
        msg.from:sendto(msg.data,msg.clientip,msg.clientport)
        --incudppkt()
        --concurrent.exit('master exit.')
      elseif msg.cmd=='close tcp' then

      elseif msg.cmd=='close udp' then

      elseif msg.cmd=='disconnect tcp client' then

      else
        error("*********************")
      end
    end
  end

end

function monitor()
  local masterpid=concurrent.spawnmonitor(master)
  concurrent.register('master',masterpid)
  --concurrent.monitor('master')
  usocks={}
  while true do
          out'm'
    local msg=concurrent.receive(1)
    --for k,v in pairs(usocks) do
    --  if not msg then print('\nudppkt:'..k:status()..'\n') end
    --end
    -- msg.signal=='EXIT' or
    if msg and (msg.signal=='DOWN') then
      out(msg.from..' exit:'..msg.reason..'.\n')
      if usocks[msg.from] then
        usocks[msg.from]:close()
        usocks[msg.from]=nil
        collectgarbage('collect')
        out'gc'
      end
      concurrent.unregister('master')
      local masterpid=concurrent.spawnlink(master)
      concurrent.register('master',masterpid)
      concurrent.monitor('master')
    elseif msg and msg.cmd=='started' then
      out'receive a started msg.\n'
      if msg.udpsock and msg.from then
        usocks[msg.from]=msg.udpsock
        out'monitor: saw a master.\n'
      end
    end
   end
end
local monitorpid=concurrent.spawn(monitor)
concurrent.register('monitor',monitorpid)



--while true do
  -- main loop
  --concurrent.step(0)-- noblock

  --out"."
  --cltime.sleep(1)
  io.flush()
--end

concurrent.loop()


--return masterpid
